# 第一排被分为两栏，第一栏的第一排是按钮“导入模板”和带文字提示“模板文件：”的显示框。点击按钮能选择模板文件（csv/xlsx/xls文件），并将文件名称显示在显示框。
# 第一排第一栏的第二排是按钮“导入数据”和带文字提示“数据文件：”的显示框。点击按钮能选择数据文件（csv/xlsx/xls文件），并将文件名称显示在显示框。
# 第一排的第二栏是两个横向分布的按钮“载入”和“导出”，这两个按钮初始都是不可点击的，且高均与左边的排控件之和一样高。
# 只有在模板文件和数据文件都被加载时，“载入”才能被点击。
# 点击“载入”后，模板文件的每一个表头都以列表的形式显示在下方。对于QListWidget中的每一行，请把表头改成同宽的按钮，
# 同时，在同一行按钮的右边添加一个显示名称为“数据源:”的显示框，再在其右边添加一个下拉选项框。下拉的数据项就是数据文件的表头。

import sys
import os
from PyQt5.QtWidgets import  QMessageBox, QSpinBox, QAction, QTextEdit, QStatusBar, QApplication, QMainWindow, QWidget, QPushButton, QLabel, QLineEdit
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QFileDialog, QListWidget, QListWidgetItem, QComboBox, QRadioButton, QButtonGroup
from PyQt5.QtCore import QDir, Qt
from PyQt5.QtGui import QIcon
import utils

help = '''准备工作：确保模板文件和数据源文件的第一行都是表头。

步骤：
1.点击“导入模板”和“导入数据”，导入导出均支持csv/xls/xlsx三种文件格式。
2.点击“载入”，相关条目会出现在下方方框中。
3.分别选择每个条目的选项：
    如果数据源选择了“[忽略]”，那么这一列将被置为空；
    如果数据源选择了“[固定值]”，那么这一列将被置为固定值输入框中的值；
    如果规则选择了“截取指定位数”，那么需要在后面的输入框中分别输入始末位置；
    ！注意：
        从第一个字符位置为1起计数始末位置，源字符串长度不足则视为空；
        如提示“配置错误”，请根据错误提示进行修改。
4.全部点击完之后，请设置“每个文件的数据条数”、“输出文件名”以及“输出文件格式”。
5.点击“导出”，选择导出路径后可再次核对信息，确认无误按“OK”即可。                  
    ！注意：
        点击“导出”弹出框的“OK”后，期间可能会出现“程序无响应”，不影响使用；
        程序运行结束后将提示拆分成功与否。
                               
反馈问题请保留当前文件夹下的“error.txt”文件。
                               '''

versionInfo = '当前版本为：1.0.4  更新日期:2024年1月26日'

updateInfo = '''
v1.0.4 2024年1月26日
新增拆分结果提示框；新增自定义截取字符串位数规则
优化配置检查提示

V1.0.3 2024年1月25日
将输出格式调整为可自定义选择

V1.0.2 2024年1月17日
将输出格式由xlsx改为xls，新增“从卡号提取地区号”以及
“从账号提取地区号”规则

V1.0.1 2024年1月16日
增加了崩溃日志记录功能，程序出错时会产生txt日志文件
功能性验证在32位Win7上运行通过

V1.0.0 2024年1月12日
功能性验证在64位Win7/Win10上运行通过
'''

def resource_path(relative_path):
    '''
    获取资源的绝对路径
    '''
    base_path = getattr(
        sys, '_MEIPASS',
        os.path.dirname(os.path.abspath(__file__))
    )
    return os.path.join(base_path, relative_path)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.dc = utils.data_config()
        # print(resource_path('splitExcel\icbc.ico'))
        self.setWindowIcon(QIcon(resource_path('icbc.ico')))

# ------------------------------------------------------------
        bar = self.menuBar()
        file = bar.addMenu("帮助")
        file.addAction("使用说明")
        file.addAction("查看版本")
        file.addAction("更新记录")
        # file.addAction("remove")
        file.triggered[QAction].connect(self.processtrigger)
        self.setCentralWidget(QTextEdit())
            
        self.statusBar = QStatusBar()
        # self.b = QPushButton("click here")
        # self.setWindowTitle("QStatusBar Example")
        self.setStatusBar(self.statusBar)
# ------------------------------------------------------------
		

        # 设置窗口标题
        self.setWindowTitle("表格-按任意模板拆分")

        # 创建主窗口的中心部件
        self.central_widget = QWidget(self)

        # 创建布局
        self.main_layout = QVBoxLayout()

        # 创建第一排布局
        self.row1_layout = QHBoxLayout()

        # 创建"导入模板"按钮
        self.template_button = QPushButton("导入模板")
        self.template_button.clicked.connect(self.load_template_file)

        # 创建显示框标签和文本框
        self.template_label = QLabel("模板文件：")
        self.template_textbox = QLineEdit()
        self.template_textbox.setReadOnly(True)

        # 将按钮、标签和文本框添加到第一排布局
        self.row1_layout.addWidget(self.template_label)
        self.row1_layout.addWidget(self.template_textbox)
        self.row1_layout.addWidget(self.template_button)

        # 创建第二排布局
        self.row2_layout = QHBoxLayout()

        # 创建"导入数据"按钮
        self.data_button = QPushButton("导入数据")
        self.data_button.clicked.connect(self.load_data_file)

        # 创建显示框标签和文本框
        self.data_label = QLabel("数据文件：")
        self.data_textbox = QLineEdit()
        self.data_textbox.setReadOnly(True)

        # 将按钮、标签和文本框添加到第二排布局
        self.row2_layout.addWidget(self.data_label)
        self.row2_layout.addWidget(self.data_textbox)
        self.row2_layout.addWidget(self.data_button)

        # 创建第三排布局
        self.row3_layout = QHBoxLayout()

        # 创建"载入"按钮
        self.load_button = QPushButton("载入")
        self.load_button.setEnabled(False)
        self.load_button.clicked.connect(self.load_template_headers)

        # 将按钮添加到第三排布局
        self.row3_layout.addWidget(self.load_button)

        # 创建列表控件
        self.headers_list = QListWidget()

        # 将第一排布局、第二排布局、第三排布局和列表控件添加到主布局
        self.main_layout.addLayout(self.row1_layout)
        self.main_layout.addLayout(self.row2_layout)
        self.main_layout.addLayout(self.row3_layout)
        self.main_layout.addWidget(self.headers_list)

        # 创建第五排布局
        self.row5_layout = QHBoxLayout()

        # 创建"导出"按钮
        self.export_button = QPushButton("导出")
        self.export_button.setEnabled(False)
        # self.export_button.clicked.connect(self.print_all_text)
        self.export_button.clicked.connect(self.set_output_path)
        
        # 添加文本输入框到每个列表项的右侧
        self.amount_spinbox = QSpinBox()
        self.amount_spinbox.setFixedWidth(50)
        self.amount_spinbox.setMinimum(1)
        self.amount_spinbox.setMaximum(1000)
        self.amount_spinbox_l1 = QLabel("每个文件的数据条数:")
        self.amount_spinbox_l1.setFixedWidth(112)
        # 创建显示框标签和文本框
        self.filename_label = QLabel("输出文件名:")
        self.filename_label.setFixedWidth(65)
        self.filename_textbox = QLineEdit()
        self.filename_textbox.setText("（输出文件名）")
        self.filename_textbox.setFixedWidth(120)
        self.filename_textbox.setReadOnly(False)

        # 添加单选框
        self.format_label = QLabel("输出格式:")
        self.format_label.setFixedWidth(55)
        self.radio_button_xls = QRadioButton()
        self.radio_button_xls.setText("xls")
        self.radio_button_xls.setChecked(True)
        self.radio_button_xls.setFixedWidth(45)
        self.radio_button_xls.setProperty("index", 0)
        # 连接 clicked 信号到槽函数
        self.radio_button_xls.clicked.connect(lambda:self.handleFormatRadioButtonClicked(self.radio_button_xls))
        # 添加单选框
        self.radio_button_xlsx = QRadioButton()
        self.radio_button_xlsx.setText("xlsx")
        self.radio_button_xlsx.setChecked(False)
        self.radio_button_xlsx.setFixedWidth(45)
        self.radio_button_xlsx.setProperty("index", 1)
        # 连接 clicked 信号到槽函数
        self.radio_button_xlsx.clicked.connect(lambda:self.handleFormatRadioButtonClicked(self.radio_button_xlsx))
        # 添加单选框
        self.radio_button_csv = QRadioButton()
        self.radio_button_csv.setText("csv")
        self.radio_button_csv.setChecked(False)
        self.radio_button_csv.setFixedWidth(45)
        self.radio_button_csv.setProperty("index", 2)
        # 连接 clicked 信号到槽函数
        self.radio_button_csv.clicked.connect(lambda:self.handleFormatRadioButtonClicked(self.radio_button_csv))
        # 分组
        self.format_group = QButtonGroup(self.central_widget)
        self.format_group.addButton(self.radio_button_csv)
        self.format_group.addButton(self.radio_button_xls)
        self.format_group.addButton(self.radio_button_xlsx)

        # 将按钮添加到第五排布局
        self.row5_layout.addWidget(self.amount_spinbox_l1)
        self.row5_layout.addWidget(self.amount_spinbox)
        self.row5_layout.addWidget(self.filename_label)
        self.row5_layout.addWidget(self.filename_textbox)
        self.row5_layout.addWidget(self.format_label)
        self.row5_layout.addWidget(self.radio_button_xls)
        self.row5_layout.addWidget(self.radio_button_xlsx)
        self.row5_layout.addWidget(self.radio_button_csv)
        self.row5_layout.addWidget(self.export_button)
        
        # 将第五排布局添加到主布局
        self.main_layout.addLayout(self.row5_layout)


        # 将主布局设置为中心部件的布局
        self.central_widget.setLayout(self.main_layout)

        # 设置中心部件
        self.setCentralWidget(self.central_widget)

    def processtrigger(self,q):
        '''
        在底部保留一个水平条作为状态栏。
        它用于显示永久或上下文状态信息。
        '''
        if (q.text() == "使用说明"):
            self.show_useful_dialog('使用说明',help)
                
        if q.text() == "查看版本":
            self.statusBar.showMessage(versionInfo,200000)

        if (q.text() == "更新记录"):
            self.show_useful_dialog('更新记录',updateInfo)            
            # self.statusBar.addWidget(self.b)
                
        # if q.text() == "remove":
        #     self.statusBar.removeWidget(self.b)
        #     self.statusBar.show()

    def handleFormatRadioButtonClicked(self,rb):
      if rb.text() == "xls":
         if rb.isChecked() == True:
            self.dc.set_export_format(rb.text())
            print (rb.text()+" is selected")
         else:
            print (rb.text()+" is deselected")
				
      if rb.text() == "xlsx":
         if rb.isChecked() == True:
            self.dc.set_export_format(rb.text())
            print (rb.text()+" is selected")
         else:
            print (rb.text()+" is deselected")

      if rb.text() == "csv":
         if rb.isChecked() == True:
            self.dc.set_export_format(rb.text())
            print (rb.text()+" is selected")
         else:
            print (rb.text()+" is deselected")


    def load_template_file(self):
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(self, "选择模板文件", "", "Excel Files (*.xlsx *.xls);;CSV Files (*.csv)")
        if file_path:
            self.template_textbox.setText(file_path)
            self.check_files_loaded()
            self.dc.set_model(file_path)

    def load_data_file(self):
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(self, "选择数据文件", "", "Excel Files (*.xlsx *.xls);;CSV Files (*.csv)")
        if file_path:
            self.data_textbox.setText(file_path)
            self.check_files_loaded()
            self.dc.set_source(file_path)

    def check_files_loaded(self):
        template_file = self.template_textbox.text()
        data_file = self.data_textbox.text()

        if template_file and data_file:
            self.load_button.setEnabled(True)
            self.export_button.setEnabled(True)
        else:
            self.load_button.setEnabled(False)
            self.export_button.setEnabled(False)

    def load_template_headers(self):
        template_file = self.template_textbox.text()
        data_file = self.data_textbox.text()
        if template_file:
            # 清空列表控件
            self.headers_list.clear()

            # 读取模板文件，提取表头
            headers = self.dc.model_head

            i = 0
            # 将表头添加到列表控件
            for header in headers:
                item = QListWidgetItem()
                layout = QHBoxLayout()

                # 创建按钮
                button = QPushButton(header)
                button.setFixedWidth(80)
                layout.addWidget(button)

                # 创建显示框标签
                label = QLabel("数据源:")
                label.setFixedWidth(50)
                layout.addWidget(label)

                # 创建下拉选项框
                combo_box = QComboBox()
                combo_box.setObjectName(header)
                if data_file:
                    # 读取数据文件的表头
                    data_headers = self.dc.source_head
                    data_headers = ['（请选择）', "[固定值]", "[忽略]"] + data_headers
                    # 将表头添加到下拉选项框
                    combo_box.addItems(data_headers)
                combo_box.setFixedWidth(140)
                # 设置 QComboBox 对应的索引值
                combo_box.setProperty("index", i)
                # 连接 currentTextChanged 信号到槽函数
                combo_box.currentTextChanged.connect(self.columnCBselectionchange)
                layout.addWidget(combo_box)

                # 添加文本输入框到每个列表项的右侧
                line_edit = QLineEdit()
                line_edit.setText("（输入固定值）")
                line_edit.setFixedWidth(100)
                layout.addWidget(line_edit)

                # 添加单选框到每个列表项的右侧
                radio_button = QRadioButton()
                radio_button.setText("去除空格")
                radio_button.setChecked(True)
                # 为每个 QRadioButton 设置自定义属性存储 QListWidgetItem 的引用
                radio_button.setProperty("index", i)
                # 连接 clicked 信号到槽函数
                radio_button.clicked.connect(self.handleSpaceRadioButtonClicked)
                layout.addWidget(radio_button)

                # 创建规则下拉选项框
                combo_box2 = QComboBox()
                combo_box2.setObjectName(header+"规则")
                rules_file = ["(请选择提取规则)","提取全部","卡号截取地区号","账号截取地区号","截取指定位数","仅提取卡号(未上线)","仅提取账号(未上线)"]
                if rules_file:
                    # 将表头添加到下拉选项框
                    combo_box2.addItems(rules_file)
                combo_box2.setFixedWidth(140)
                # 设置 QComboBox 对应的索引值
                combo_box2.setProperty("index", i)
                # 连接 currentTextChanged 信号到槽函数
                combo_box2.currentTextChanged.connect(self.rulesCBselectionchange)
                layout.addWidget(combo_box2)

                # 创建显示框标签
                label = QLabel("自定义:")
                label.setFixedWidth(45)
                layout.addWidget(label)
               # 添加文本输入框到每个列表项的右侧
                line_edit_begin = QLineEdit()
                line_edit_begin.setText("(始)")
                line_edit_begin.setFixedWidth(30)
                layout.addWidget(line_edit_begin)
                line_edit_end = QLineEdit()
                line_edit_end.setText("(末)")
                line_edit_end.setFixedWidth(30)
                layout.addWidget(line_edit_end)

                # 设置列表控件项的布局
                widget = QWidget()
                widget.setLayout(layout)
                item.setSizeHint(widget.sizeHint())
                self.headers_list.addItem(item)
                self.headers_list.setItemWidget(item, widget)

                i += 1

    def set_output_path(self):
        to_filename = self.filename_textbox.text()
       
        if to_filename == '':
            self.show_error_dialog('输出文件名为空。')
        else:
            file_dialog = QFileDialog()
            file_dialog.setFileMode(QFileDialog.Directory)
            file_dialog.setOptions(QFileDialog.ShowDirsOnly | QFileDialog.DontUseNativeDialog)
            file_dialog.setDirectory(QDir.currentPath())  # 设置默认路径为当前路径
            file_dialog.setWindowTitle("选择输出路径")  # 设置选择框标题为中文

            # 获取固定文本
            self.get_all_fixed_text()
            # 获取每个文件需要包含的数据条数
            self.dc.count_per_file = self.amount_spinbox.value()
            # 获取自定义规则的始末
            self.get_all_rules_substr()

            result = self.dc.check()
            if result != 'checked':
                self.show_error_dialog(result)
                return

            if file_dialog.exec_():
                selected_directory = file_dialog.selectedFiles()[0]
                # 获取保存目录
                self.dc.to_path = selected_directory + '/' + to_filename # 这里不加 '.xlsx' ，因为需要加序号再加后缀
                print("Selected Output Path:", self.dc.to_path)

                self.showdialog()

    def showdialog(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)

        # 设置文本内容的换行方式为固定宽度换行
        msg.setTextFormat(Qt.RichText)
        msg.setTextInteractionFlags(Qt.TextBrowserInteraction)

        msg.setText("即将进行文件的拆分,请确保已经完整填写相关信息")
        msg.setInformativeText("可点击中间按钮查看")
        msg.setWindowTitle("重要提示")
        msg.setDetailedText(self.dc.show())
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msg.buttonClicked.connect(self.do)

        retval = msg.exec_()

    def show_error_dialog(self,text):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)

        # 设置文本内容的换行方式为固定宽度换行
        msg.setTextFormat(Qt.RichText)
        msg.setTextInteractionFlags(Qt.TextBrowserInteraction)

        msg.setText('发现错误')
        msg.setInformativeText(text)
        msg.setWindowTitle("重要提示")
  
        msg.setStandardButtons(QMessageBox.Ok)
        msg.buttonClicked.connect(self.msgbtn)

        retval = msg.exec_()
    
    def show_useful_dialog(self,title,text):
        msg = QMessageBox()
        # msg.setIcon(QMessageBox.Information)

        # 设置文本内容的换行方式为固定宽度换行
        msg.setTextFormat(Qt.RichText)
        msg.setTextInteractionFlags(Qt.TextBrowserInteraction)

        msg.setText(title)
        msg.setInformativeText(text)
        msg.setWindowTitle("重要提示")
  
        msg.setStandardButtons(QMessageBox.Ok)
        msg.buttonClicked.connect(self.msgbtn)

        retval = msg.exec_()

    @staticmethod
    def msgbtn(i):
        print("Button pressed is:",i.text())

    def do(self, i):
        print("do Button pressed is:", i.text())
        if i.text() == 'OK':
            result = self.dc.do_change_column()
            if result:
                self.show_useful_dialog('恭喜！拆分完成',f'您可以在{self.dc.to_path}查看生成的文件。')
            else:
                self.show_error_dialog('拆分失败，您可以\n1.关闭本工具尝试重新执行 \n2.连同当前目录下“error.txt”反馈给开发者')

    def rulesCBselectionchange(self, text):
        combo_box = self.headers_list.sender()  # 获取发送信号的 QComboBox
        combo_item_index =  combo_box.currentIndex()
        combo_box_index = combo_box.property("index")
        print("Item", combo_box_index, "selected text:", text, combo_item_index)
        self.dc.set_rules(combo_box_index, combo_item_index, text)

    def columnCBselectionchange(self, text):
        combo_box = self.headers_list.sender()  # 获取发送信号的 QComboBox
        combo_box_index = combo_box.property("index")
        combo_item_index =  combo_box.currentIndex()
        print("Item", combo_box_index, "selected text:", text, combo_item_index)
        self.dc.set_column(combo_box_index, combo_item_index, text)

    def handleSpaceRadioButtonClicked(self):
        radio_button = self.headers_list.sender()  # 获取发送信号的 QRadioButton
        radio_button_index = radio_button.property("index")
        if radio_button.isChecked():
            print("Item", radio_button_index, "selected")
            self.dc.set_despace(radio_button_index, True)
        else:
            print("Item", radio_button_index, "deselected")
            self.dc.set_despace(radio_button_index, False)

    def get_all_fixed_text(self):
        count = self.headers_list.count()
        for i in range(count):
            item = self.headers_list.item(i)
            widget = self.headers_list.itemWidget(item)
            # 注意 如果布局改变，这个index也要改
            line_edit = widget.layout().itemAt(3).widget()
            text = line_edit.text()
            self.dc.set_fixed_content_text(i, text=text)
            print(f"Text for index {i}: {text}")

    def get_all_rules_substr(self):
        count = self.headers_list.count()
        for i in range(count):
            item = self.headers_list.item(i)
            widget = self.headers_list.itemWidget(item)
            # 注意 如果布局改变，这个index也要改
            # begin
            line_edit_begin = widget.layout().itemAt(7).widget()
            text_begin = line_edit_begin.text()
            # end
            line_edit_end = widget.layout().itemAt(8).widget()
            text_end = line_edit_end.text()
            try:
                if text_begin != '(始)' and text_end != '(末)':
                    begin = int(text_begin)
                    end = int(text_end)
                    self.dc.set_rules_substr(i, begin, end)

                    print(f"Begin & end for index {i}: {begin} - {end}")
            except Exception as e:
                self.show_error_dialog(f"第{i+1}行的自定义规则的始末位置的输入有误！\n 调试信息:{e}")
        print('get_all_rules_substr DONE')    

# class TimerThread(QThread):
#     # 子类化一个QThread
#     # QThread继承自QObject，是可以定义信号的
#     signal_timer_cur_time = pyqtSignal(int)
 
#     def __init__(self):
#         super().__init__()
#         self.cur_time = 0
 
#     def run(self) -> None:
#         self.cur_time = 0
#         # QThread可以通过terminate强制终止，这里就没用标志位控制退出了
#         while True:
#             self.signal_timer_cur_time.emit(self.cur_time)
#             # time.sleep(0.3)
#             self.cur_time += 1

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.resize(680, 500)  # 设置窗口的宽度为 800 像素
    window.show()
    sys.exit(app.exec_())